<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue指令练习</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
    <style>
        * { margin: 0; padding: 0; }
        #app { 
            font-family: sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            width: 600px;
            margin: 50px auto 0;
            position: relative;
        }
        .toggle-button {
            background: #1188FF;
            color: #FFF;
            border: 0;
            border-radius: 2px;
            padding: 10px;
            font-size: 14px;
            cursor: pointer;
            outline: none;
            transition: 0.2s all;
        }

        .toggle-button:hover {
            background: #1080F8;
        }

        .popup-box {
            position: absolute;
            left: 50%;
            top: 50px;
            transform: translateX(-50%);
            background: #F0F8FF;
            border-radius: 1px;
            box-shadow: 1px 1px 15px 0 rgba(0,0,0,0.2);
            padding: 40px;
            color: #555585;
        }
    </style>
</head>
<body>
    <div id="app">
        <button
            ref="button"
            class="toggle-button"
            @click="showPopup = !showPopup"
        >TOGGLE</button>
        <div
            v-show="showPopup"
            v-closable="{
                exclude: ['button'],
                handler: 'onClose'
            }"
            class="popup-box"
        >
            Test Popup Box
        </div>
    </div>
    <script>
        Vue.directive('closable', {
            bind: function (el, binding, vnode) {
                function handleOutsideClick(e) {
                    e.stopPropagation();
                    const { handler, exclude } = binding.value;
                    let clickedOnExcludedEl = false;
                    exclude.forEach(refName => {
                        if (!clickedOnExcludedEl) {
                            const excludedEl = vnode.context.$refs[refName];
                            clickedOnExcludedEl = excludedEl.contains(e.target);
                        }
                    });
                    if (!el.contains(e.target) && !clickedOnExcludedEl) {
                        vnode.context[handler]();
                    }
                };
                el.__handleOutsideClick__ = handleOutsideClick;
                document.addEventListener('click', handleOutsideClick);
                document.addEventListener('touchstart', handleOutsideClick);                
            },
            unbind(el) {
                document.removeEventListener('click', el.__handleOutsideClick__);
                document.removeEventListener('touchstart', el.__handleOutsideClick__);
                delete el.__handleOutsideClick__;
            }
        });
        new Vue({
            el: '#app',
            data: {
                showPopup: false
            },
            methods: {
                onClose() {
                    this.showPopup = false;
                }
            }
        })
    </script>
</body>
</html>